//
//  BKHttpRequestManager.m
//  BCommon
//
//  Created by baboy on 4/1/15.
//  Copyright (c) 2015 baboy. All rights reserved.
//

#import "BKHttpRequestManager.h"
#import "BKHttpRequestQueue.h"
#import "NSURLSessionTask+x.h"
#import "BKKitDefines.h"
#import "FileCache.h"
#import "RegexKitLite.h"
#import "NSString+x.h"
#import "NSDictionary+x.h"
#import "NSArray+x.h"
#import "NSData+x.h"
#import "HttpResponse.h"

@interface BKHttpRequestManager()
//@property (nonatomic, strong) BKHttpRequestQueue *requestQueue;
@end

@implementation BKHttpRequestManager
+ (id)lock {
    static id _lock = nil;
    static dispatch_once_t initOnceLock;
    dispatch_once(&initOnceLock, ^{
        _lock = [[NSRecursiveLock alloc] init];
    });
    return _lock;
}
+ (id)defaultManager {
    static id _defaultHttpRequestManager = nil;
    static dispatch_once_t initOnceHttpRequestManager;
    dispatch_once(&initOnceHttpRequestManager, ^{
        _defaultHttpRequestManager = [[BKHttpRequestManager alloc] init];
    });
    
    return _defaultHttpRequestManager;
}

+ (_Nonnull id)manager{
    return [[BKHttpRequestManager alloc] init];
}
- (id)init{
    if (self = [super init]) {
        self.responseSerializer.acceptableContentTypes = nil;
//        self.requestQueue = [[BKHttpRequestQueue alloc] init];
        self.requestSerializer = [AFJSONRequestSerializer serializer];
        self.requestSerializer.timeoutInterval = 90.0f;
    }
    return self;
}

- (BOOL)cancelTask:(BKHttpRequestTask *_Nullable)task{
//    [self.requestQueue cancelTask:task];
    return YES;
}
- (void)setCacheRequestIgnoreParams:(NSArray *)cacheRequestIgnoreParams{
    _cacheRequestIgnoreParams = cacheRequestIgnoreParams;
}
- (NSString *)getRequestCachePath:(NSString *)url{
    NSString *u = url;
    for (NSString *field in self.cacheRequestIgnoreParams) {
        NSString *f = [field stringByReplacingOccurrencesOfRegex:@"\\*" withString:@"[0-9a-zA-Z_-]+"];
        NSString *re = [NSString stringWithFormat:@"(%@=[^=&]{0,}&?)",f];
//        NSArray *arr = [u arrayOfCaptureComponentsMatchedByRegex:re];
        u = [u stringByReplacingOccurrencesOfRegex:re withString:@""];
    }
    NSString *fp = getHttpRequestCacheFilePath([NSURL URLWithString:u]);
    return fp;
}
- (nullable id )getData:(nullable NSString *)URLString
             parameters:(nullable id)parameters
            cachePolicy:(BKHttpRequestCachePolicy)cachePolicy
                success:(nullable void (^)(id _Nonnull task, id _Nullable json))success
                failure:(nullable void (^)(id _Nullable task,id _Nullable json, NSError * _Nonnull error))failure{
    //NSURLSessionDataTask
    NSURLSessionTask *task = [super GET:URLString
           parameters:parameters
             progress:nil
              success:^(NSURLSessionDataTask * _Nullable dataTask, id _Nonnull json){
                  success(dataTask,json);
                  if (cachePolicy & BKHttpRequestCachePolicyCached) {
                      if (json) {
                          
                          NSString *fp = getHttpRequestCacheFilePath([NSURL URLWithString:[URLString URLStringWithParam:parameters]]);
                          [[json jsonData] writeToFile:fp atomically:YES];
                          DLOG(@"%@%lld",fp,[fp sizeOfFile]);
                      }
                  }
                  [self.session finishTasksAndInvalidate];
              }
              failure:^(NSURLSessionDataTask * _Nullable dataTask, NSError * _Nonnull error){
                  id json = nil;
                  if (cachePolicy & BKHttpRequestCachePolicyFallbackToCacheIfLoadFails) {
                      NSData *data = getHttpRequestCacheFileData([NSURL URLWithString:[URLString URLStringWithParam:parameters]]);
                      if (data) {
                          DLOG(@"read cache:%@",getHttpRequestCacheFilePath([NSURL URLWithString:[URLString URLStringWithParam:parameters]]));
                          json = [data json];
                      }
                  }
                  failure(dataTask,json,error);
                  [self.session finishTasksAndInvalidate];
              }];
    return task;

}
- (id)getJSON:(NSString *)URLString
   parameters:(id)parameters
  cachePolicy:(BKHttpRequestCachePolicy)cachePolicy
      success:(void (^)(id _Nonnull, id _Nullable))success
      failure:(void (^)(id _Nullable,id _Nullable, NSError * _Nonnull))failure
{
    BKHttpRequestTask *task = nil;
    BKHttpRequestPool *pool = [BKHttpRequestPool defaultPool];
    @synchronized(pool) {
        
        id p = parameters;
        if (![p isMemberOfClass:[NSDictionary class]] && [p respondsToSelector:@selector(dict)]) {
            p = [p dict];
        }
        
        DLOG(@"url:%@, param:%@", URLString, p);
        NSString *url = [URLString URLStringWithParam: p];
        DLOG(@"url:%@", url);
        NSString *identifier = url;
//        NSUUID *uuid = [NSUUID UUID];
//
//        BHttpResponseHandler *handler = [[BHttpResponseHandler alloc] initWithUUID:uuid
//                                                                           success:success
//                                                                           failure:failure];
//        BKHttpRequestRelativeTask *relativeTask = [self.requestQueue taskForIdentifier:identifier];
//        if (relativeTask) {
//            [relativeTask addHandler:handler];
//            task = [[BKHttpRequestTask alloc] initWithTask:relativeTask.task
//                                                                       UUID:uuid
//                                                                 identifier:identifier];
//            task.delegate = self;
//            return task;
//        }
        task = [[BKHttpRequestTask alloc] initWithIdentifier:identifier];
        task.successBlock = success;
        task.failureBlock = failure;
        task.delegate = self;
        [pool addTask:task];
        
        NSURLSessionTask *sessionTask = [pool sessionTaskForIdentifier:identifier];
        if (!sessionTask) {
            sessionTask = [super GET:url
                          parameters:nil
                            progress:nil
                             success:^(NSURLSessionDataTask * _Nullable dataTask, id _Nonnull json){
                                 //success(dataTask,json);
                                 @synchronized(pool) {
                                     [pool clearTasksWithIdentifier:identifier
                                                       runWithBlock:^(BKHttpRequestTask * _Nonnull item)  {
                                                           item.successBlock(dataTask, json);
                                                       }];
                                     if (cachePolicy & BKHttpRequestCachePolicyCached) {
                                         if (json) {
                                             NSString *fp = [self getRequestCachePath:url];
                                             [[json jsonData] writeToFile:fp atomically:YES];
                                         }
                                     }
                                     [self.session finishTasksAndInvalidate];
                                 }
                             }
                             failure:^(NSURLSessionDataTask * _Nullable dataTask, NSError * _Nonnull error){
                                 id json = nil;
                                 if (cachePolicy & BKHttpRequestCachePolicyFallbackToCacheIfLoadFails) {
                                     NSString *fp = [self getRequestCachePath:url];
                                     NSData *data = [fp fileData];
                                     if (data) {
                                         DLOG(@"read cache:%@",getHttpRequestCacheFilePath([NSURL URLWithString:[URLString URLStringWithParam:parameters]]));
                                         json = [data json];
                                     }
                                 }
                                 @synchronized(pool) {
                                     [pool clearTasksWithIdentifier:identifier
                                                       runWithBlock:^(BKHttpRequestTask * _Nonnull item)  {
                                                           item.failureBlock(dataTask,json,error);
                                                       }];
                                     [self.session finishTasksAndInvalidate];
                                 }
                             }];
            
        }
        task.sessionTask = sessionTask;
    }
    return task;
}
- (id)getJSON:(NSString *)URLString
   parameters:(id)parameters
      success:(void (^)(id _Nonnull, id _Nullable))success
      failure:(void (^)(id _Nullable,id _Nullable, NSError * _Nonnull))failure
{
    return [self getJSON:URLString
              parameters:parameters
             cachePolicy:BKHttpRequestCachePolicyNone
                 success:success
                 failure:failure];
}

- (nullable id)POST:(NSString *)URLString
         parameters:(nullable id)parameters
       responseType:(BKHttpResponseType)responseType
            success:(nullable void (^)(id _Nonnull task, id _Nullable responseObject))success
            failure:(nullable void (^)(id _Nonnull task, NSError *error))failure{
    NSURLSessionDataTask *sessionTask = [self POST:URLString
                                        parameters:parameters
                                          progress:^(NSProgress * _Nonnull uploadProgress) {
                                              
                                          }
                                           success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                                               success(task, responseObject);
                                           }
                                           failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                                               failure(task, error);
                                           }];
    
    NSString *identifier = URLString;
    NSUUID *uuid = [NSUUID UUID];
    
    BKHttpRequestTask *task = [[BKHttpRequestTask alloc] initWithTask:sessionTask
                                              UUID:uuid
                                        identifier:identifier];
    
    return task;
}
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure{
    NSURLSessionDataTask *sessionTask = [self POST:URLString
                                       parameters:parameters
                                         progress:^(NSProgress * _Nonnull uploadProgress) {
                                             
                                         }
                                          success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                                              success(task, responseObject);
                                              [self.session finishTasksAndInvalidate];
                                          }
                                          failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                                              failure(task, error);
                                              [self.session finishTasksAndInvalidate];
                                          }];
    return sessionTask;
}
#pragma Download
- (nullable id)download:(NSString *_Nullable)URLString
            cachePolicy:(BKHttpRequestCachePolicy)cachePolicy
               userInfo:(id _Nullable)userInfo
               progress:(nullable void (^)(NSProgress * _Nullable downloadProgress)) downloadProgressBlock
                success:(nullable void (^)(id _Nullable task, id _Nullable fp))success
                failure:(nullable void (^)(id _Nullable task,id _Nullable fp, NSError * _Nonnull error))failure{
    
    BKHttpRequestTask *task = nil;
    BKHttpRequestPool *pool = [BKHttpRequestPool defaultPool];
    @synchronized(pool) {
        if (cachePolicy & BKHttpRequestCachePolicyLoadIfNotCached) {
            NSString *fp = getHttpRequestCacheFilePath([NSURL URLWithString:URLString]);
            if ([fp sizeOfFile]>0) {
                success(nil,[NSURL fileURLWithPath:fp]);
                DLOG(@"download,read from cache:%lld,%@",[fp sizeOfFile],[fp lastPathComponent]);
                return nil;
            }
        }
        NSString *identifier = URLString;
        
        task = [[BKHttpRequestTask alloc] initWithIdentifier:identifier];
        task.successBlock = success;
        task.failureBlock = failure;
        task.delegate = self;
        [pool addTask:task];
        
        NSURLSessionDownloadTask *sessionTask = [pool sessionTaskForIdentifier:identifier];
        if(!sessionTask){
            NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:URLString]];
            sessionTask = [super downloadTaskWithRequest:request
                                                                          progress:downloadProgressBlock
                                                                       destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
                                                                           return [NSURL fileURLWithPath:getHttpRequestCacheFilePath([NSURL URLWithString:URLString])];
                                                                       }
                                                                 completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
                                                                     @synchronized(pool) {
                                                                         [pool clearTasksWithIdentifier:identifier
                                                                                           runWithBlock:^(BKHttpRequestTask * _Nonnull item) {
                                                                                               if (error) {
                                                                                                   item.failureBlock(nil,filePath,error);
                                                                                               }
                                                                                               if(!error){
                                                                                                   item.successBlock(nil,filePath);
                                                                                               }
                                                                                           }];
                                                                     }//end synchronized
                                                                 }];
            [sessionTask resume];
        }
        task.sessionTask = sessionTask;
    }
    return task;

}
- (nullable id)download:(NSString *_Nullable)URLString
            cachePolicy:(BKHttpRequestCachePolicy)cachePolicy
               progress:(nullable void (^)(NSProgress * _Nullable downloadProgress)) downloadProgressBlock
                success:(nullable void (^)(id _Nonnull task, id _Nullable fp))success
                failure:(nullable void (^)(id _Nullable task,id _Nullable fp, NSError * _Nonnull error))failure{
    return [self download:URLString
              cachePolicy:cachePolicy
                 userInfo:nil
                 progress:downloadProgressBlock
        success:success
                  failure:failure];
}

- (nullable id)download:(NSString *_Nullable)URLString
               progress:(nullable void (^)(NSProgress * _Nullable downloadProgress)) downloadProgressBlock
                success:(nullable void (^)(id _Nonnull task, id _Nullable fp))success
                failure:(nullable void (^)(id _Nullable task,id _Nullable fp, NSError * _Nonnull error))failure{
    return [self download:URLString
              cachePolicy:BKHttpRequestCachePolicyLoadIfNotCached
                 progress:downloadProgressBlock
        success:success
                  failure:failure];
}
@end
